<?php
/**
 * RAI(Remote Access Interface) XmlRpc格式定义类
 *
 *
 * @author iwind <iwind.iwind@gmail.com>
 * @link http://www.4kiki.net/
 * @version $Id: RaiXmlRpcFormat.php 312 2008-04-23 16:29:38Z iwind $
 * @package net.4kiki.rai.format
 */


import("net.4kiki.rai.format.xmlrpc.*");

/**
 * RAI XmlRpc数据格式
 * 
 * http://www.xmlrpc.com/spec
 *
 * @version $Id: RaiXmlRpcFormat.php 312 2008-04-23 16:29:38Z iwind $
 * @package net.4kiki.rai.format
 * @since 0.0.3
 */
class RaiXmlRpcFormat implements RaiFormat {
	private $charset;
	
	/**
	 * 构造器
	 *
	 * @param string $charset 字符集
	 */
	public function __construct($charset = "UTF-8") {
		$this->charset = $charset;
	}
	
	/**
	 * 解码请求
	 *
	 * @param string $data 请求编码后的字符串
	 * @return RaiRequest
	 */
	public function decodeRequest($data) {
		$request = new RaiRequest();
		$document = new DOMDocument();
		$document->loadXml($data);
		$xpath = new DOMXPath($document);
		$methodNamePath = $xpath->query("/methodCall/methodName");
		if ($methodNamePath->length > 0) {
			$methodNameString = $methodNamePath->item(0)->nodeValue;
			$pieces = explode(".", $methodNameString);
			if (!empty($pieces)) {
				$request->setMethodName(array_pop($pieces));
				$request->setServiceName(implode(".", $pieces));
			}
		}
		$paramPath = $xpath->query("/methodCall/params/param/value/*");
		$arguments = array();
		if ($paramPath->length > 0) {
			foreach ($paramPath as $paramNode) {
				$arguments[] = XmlRpcHelper::getDecoder($paramNode->nodeName, $document->saveXML($paramNode), $document->xmlEncoding)->getValue();
			}
		}
		$request->setArguments($arguments);
		return $request;
	}
	
	/**
	 * 编码请求
	 *
	 * @param RaiRequest $request 请求数据对象
	 * @return string
	 */
	public function encodeRequest(RaiRequest $request) {
		$document = new DOMDocument("1.0", $this->charset);
		$rootElement = $document->appendChild($document->createElement("methodCall"));
		$methodElement = $rootElement->appendChild($document->createElement("methodName", $request->getServiceName() . "." . $request->getMethodName()));
		$paramsElement = $rootElement->appendChild($document->createElement("params"));
		foreach ($request->getArguments() as $argument) {
			$paramElement = $paramsElement->appendChild($document->createElement("param"));
			$valueElement = $paramElement->appendChild($document->createElement("value"));
			$valueElement->appendChild($document->importNode(XmlRpcHelper::getEncoder($argument, $this->charset)->asDOM()->firstChild, true));
		}
		return $document->saveXML();
	}
	
	/**
	 * 解码响应
	 *
	 * @param string $data 响应编码后的字符串
	 * @return RaiResponse
	 */
	public function decodeResponse($data) {
		$data = XmlRpcHelper::checkXml($data);
		$document = new DOMDocument();
		if (!@$document->loadXml($data)) {
			throw new RaiException("can't parse response data '{$data}'");
		}
		$xpath = new DOMXPath($document);
		$paramsNodes = $xpath->query("/methodResponse/params");
		$faultNodes = $xpath->query("/methodResponse/fault");
		$response = new RaiResponse(0, null);
		if ($paramsNodes->length > 0) {
			$paramNodes = $xpath->query("param/value/*", $paramsNodes->item(0));
			if ($paramNodes->length > 0) {
				$valueNode = $paramNodes->item(0);
				$response->setData(XmlRpcHelper::getDecoder($valueNode->nodeName, $document->saveXML($valueNode), $document->xmlEncoding)->getValue());
			}
		}
		else {
			$faultNode = $xpath->query("value/*", $faultNodes->item(0));
			$fault = new RaiFault();
			if ($faultNode->length > 0) {
				$valueNode = $faultNode->item(0);
				$array = XmlRpcHelper::getDecoder($valueNode->nodeName, $document->saveXML($valueNode), $document->xmlEncoding)->getValue();
				if (array_key_exists("faultCode", $array)) {
					$fault->setCode($array["faultCode"]);
				}
				if (array_key_exists("faultString", $array)) {
					$fault->setMessage($array["faultString"]);
				}
				$response->setFault($fault);
			}
		}
		return $response;
	}
	
	/**
	 * 编码响应
	 *
	 * @param RaiResponse $response 响应数据对象
	 * @return string
	 */
	public function encodeResponse(RaiResponse $response) {
		$document = new DOMDocument("1.0", $this->charset);
		$rootElement = $document->appendChild($document->createElement("methodResponse"));
		$fault = $response->getFault();
		if ($fault === null) {
			$paramsElement = $rootElement->appendChild($document->createElement("params"));
			$paramElement = $paramsElement->appendChild($document->createElement("param"));
			$valueElement = $paramElement->appendChild($document->createElement("value"));
			$valueElement->appendChild($document->importNode(XmlRpcHelper::getEncoder($response->getData(), $this->charset)->asDOM()->firstChild, true));
		}
		else {			
			$faultElement = $rootElement->appendChild($document->createElement("fault"));
			$valueElement = $faultElement->appendChild($document->createElement("value"));
			$valueElement->appendChild($document->importNode(XmlRpcHelper::getEncoder(array("faultCode"=>$fault->getCode(), "faultString"=>$fault->getMessage()), $this->charset)->asDOM()->firstChild, true));;
		}
		return $document->saveXML();
	}
	
	/**
	 * 取得内容类型
	 */
	public function getContentType() {
		return "text/xml;charset=" . $this->charset;
	}
}

?>